package cn.crsc.openlookeng.handler;

import cn.crsc.openlookeng.adhoc.jdbc.EngineType;
import cn.crsc.openlookeng.adhoc.jdbc.QueryObject;
import cn.crsc.openlookeng.adhoc.query.JsonUtil;
import cn.crsc.openlookeng.adhoc.query.QueryService;
import cn.crsc.openlookeng.annotation.Catalog;
import cn.crsc.openlookeng.annotation.Query;
import cn.crsc.openlookeng.annotation.Schema;
import cn.crsc.openlookeng.domain.PageRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 动态代理，需要注意的是，这里用到的是JDK自带的动态代理，代理对象只能是接口，不能是类
 */

@Slf4j
public class QueryProxy<T> implements InvocationHandler {

    private LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
 
    private Class<T> interfaceType;
 
    public QueryProxy(Class<T> intefaceType) {
        this.interfaceType = interfaceType;
    }


    /**
     * interface ParameterizedType extends Type {
     *      //获取参数类型<>里面的那些值,例如Map<K,V> 那么就得到 [K,V]的一个数组
     *      Type[] getActualTypeArguments();
     *      //获取参数类型<>前面的值，例如例如Map<K,V> 那么就得到 Map
     *      Type getRawType();
     *      //获取其父类的类型，例如Map 有一个内部类Entry,  那么在Map.Entry<K,V> 上调用这个方法就可以获得 Map
     *      Type getOwnerType();
     * }
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this,args);
        }
        List<String> paramterList = new ArrayList<>();
        QueryObject queryObject = new QueryObject();
        Catalog catalog = AnnotationUtils.findAnnotation(method, Catalog.class);
        if(null != catalog){
            queryObject.setEngineType(catalog.value());
        }

        Schema schema = AnnotationUtils.findAnnotation(method, Schema.class);
        if(null != schema){
            if(catalog == null){
                throw new RuntimeException("Schema need Catalog");
            }
            queryObject.setSchema(schema.value());
        }

        Query query = AnnotationUtils.findAnnotation(method, Query.class);
        String sql = query.value();

        //直接通过method就能拿到所有的参数
        Parameter[] params = method.getParameters();
        for (Parameter parameter : params) {
            paramterList.add(parameter.getName());
        }
        Type genericReturnType = method.getGenericReturnType();
        //泛型 返回 值
       Class returnType = null;
       Class parameterizedType = null;
       // 获取泛型类型
        if (genericReturnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();

            if(actualTypeArguments[0] instanceof ParameterizedType){
                parameterizedType = (Class)((ParameterizedType) actualTypeArguments[0]).getRawType();
            }else {
                parameterizedType = (Class) actualTypeArguments[0];
            }

        }
        // 获取返回值类型
        returnType = method.getReturnType();


        Properties properties = new Properties();
        if(ArrayUtils.isNotEmpty(args)){
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                if(arg instanceof PageRequest){
                    queryObject.setPageSize(((PageRequest) arg).getSize());
                    queryObject.setPageNum(((PageRequest) arg).getPage());
                }else if(BeanUtils.isSimpleProperty(arg.getClass())){
                    properties.put(paramterList.get(i),String.valueOf(arg));
                }else {
                    Map<String, Object> paramMap = JsonUtil.bean2Map(arg);
                    paramMap.entrySet().stream().forEach(entry ->{
                        properties.put(entry.getKey(),String.valueOf(entry.getValue()));
                    });

                }
            }
        }

        String replaceSql = PropertyPlaceHolderUtil.replace(sql, properties);
        QueryService queryService = SpringUtils.getBean(QueryService.class);
        log.info("sql：{}",replaceSql);
        Properties props=System.getProperties();
        queryObject.setCurrentUser(props.getProperty("user.name"));
        queryObject.setSql(replaceSql);
        Object entity = queryService.executeQueryForEntity(queryObject, returnType,parameterizedType);
        return entity;
    }
}